/*
 * $Id: time_utime1.c,v 1.3 2005-09-11 15:15:35 bacon Exp $
 */

#include <xp/bas/time.h>

/*
 * localtime_r.c
 * Original Author:	Adapted from tzcode maintained by Arthur David Olson.
 *
 * Converts the calendar time pointed to by tim_p into a broken-down time
 * expressed as local time. Returns a pointer to a structure containing the
 * broken-down time.
 */

#define IS_LEAPYEAR(y) \
	((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)

#ifdef __BORLANDC__
static int mon_lengths[2][(int)XP_MON_IN_YEAR] = {
#else
static int mon_lengths[2][XP_MON_IN_YEAR] = {
#endif
	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

static int year_lengths[2] = { 365, 366 };

int xp_time_to_utime (xp_time_t t, xp_utime_t* ut)
{
	xp_time_t days, rem;
	int y;
	int yleap;
	const int *ip;

	days = t / XP_MSEC_IN_DAY;
	rem = t % XP_MSEC_IN_DAY;

	while (rem < 0) {
		rem += XP_MSEC_IN_DAY;
		days--;
	}

	while (rem >= XP_MSEC_IN_DAY) {
		rem -= XP_MSEC_IN_DAY;
		days++;
	}
 
	/* compute hour, min, and sec */  
	ut->hour = rem / XP_MSEC_IN_HOUR;
	rem %= XP_MSEC_IN_HOUR;
	ut->min = rem / XP_MSEC_IN_MIN;
	rem %= XP_MSEC_IN_MIN;
	ut->sec = rem / XP_MSEC_IN_SEC;
	ut->msec = rem % XP_MSEC_IN_SEC;

	/* compute day of week */
	ut->wday = (XP_EPOCH_WDAY + days) % XP_DAY_IN_WEEK;
	if (ut->wday < 0) ut->wday += XP_DAY_IN_WEEK;

	/* compute year & day of year */
	y = XP_EPOCH_YEAR;
	if (days >= 0) {
		for (;;) {
			yleap = IS_LEAPYEAR(y);
			if (days < year_lengths[yleap]) break;
			y++;
			days -= year_lengths[yleap];
		}
	}
	else {
		do {
			y--;
			yleap = IS_LEAPYEAR(y);
			days += year_lengths[yleap];
		} while (days < 0);
	}

	ut->year = y - XP_UTIME_BASE_YEAR;
	ut->yday = days;
	ip = mon_lengths[yleap];
	for (ut->mon = 0; days >= ip[ut->mon]; ++ut->mon)
		days -= ip[ut->mon];
	ut->day = days + 1;

	return 0;
}
